package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

/*
	1.使用io.WriteString写文件
*/
var filedatapath = "D:\\go\\src\\code.oldboyedu.com\\studygo\\day03\\person.txt"

func createdata() []map[string]interface{} {
	test := make([]map[string]interface{}, 0)
	testmap := make(map[string]interface{})
	testmap["worker"] = "ops"
	testmap["name"] = "ops"
	testmap["hobbies"] = []string{"ops", "jk"}
	test = append(test, testmap)
	testmap["worker"] = "dev"
	testmap["name"] = "jk"
	testmap["hobbies"] = []string{"ops", "jk", "hs"}
	test = append(test, testmap)
	testmap["worker"] = "dev"
	testmap["name"] = "jk"
	testmap["hobbies"] = []string{"ops", "jk", "hs"}
	test = append(test, testmap)
	return test
}
func Exists(path string) bool {
	/*
		该段代码的功能是通过传入进来的形参判断保存数据的文件或目录是否存在.
		如果通过os.Stat()函数获取到了文件信息，则返回true 文件存在
		如果未获取到文件信息，则通过os.IsExist()函数判断err是否表示指定的文件或目录已存在，如果已存在，返回true 反之返回false
	*/
	_, err := os.Stat(path) //os.Stat获取文件信息
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		return false
	}
	return true
}

/*
 writefile() file.WriteString方法写文件
	通过Exists()函数来确认文件是否存在
		如果存在，则使用追加的方法打开文件，并通过file.WriteString()函数写入字符串类型的数据
		反之，则使用os.Create()函数创建文件，并通过file.WriteString()函数写入字符串类型的数据
 Readfile()  file.Read方法读取文件
	通过Exists()函数来确认文件是否存在
		如果存在，则使用os.Open()函数打开文件，并通过死循环for和 file.Read()函数读取数据
		反之，则使用os.Create()函数创建文件，并通过file.WriteString()函数写入字符串类型的数据
*/
func writefile(fileName, persondata string) {
	if Exists(fileName) {
		file, err := os.OpenFile(fileName, os.O_APPEND, 0666)
		if err != nil {
			fmt.Println("追加打开文件失败")
			return
		}
		n, err := file.WriteString(persondata)
		defer file.Close()
		if err != nil {
			fmt.Println("写文件失败")
			return
		}
		file.WriteString("\n") //每次写入时换行写入
		fmt.Println(n)
	} else {
		file, err := os.Create(fileName)
		if err != nil {
			fmt.Println("创建文件失败")
			return
		}
		n, err := file.WriteString(persondata)
		if err != nil {
			fmt.Println("写文件失败")
			return
		}
		file.WriteString("\n")
		fmt.Println(n)
		defer file.Close()
	}
}
func Readfile(fileName string) []map[string]interface{} {
	if Exists(fileName) {
		file, err := os.Open(fileName)
		if err != nil {
			fmt.Println(err)
			return nil
		}
		defer file.Close()
		var chunk []byte
		buf := make([]byte, 2048) //把文件写到缓存中，再将缓存中的文件追击到字节类型的切片中
		for {
			n, err := file.Read(buf)
			if err != nil && err != io.EOF {
				fmt.Println(err)
				return nil
			}
			if n == 0 {
				break
			}
			chunk = append(chunk, buf[:n]...)
		}
		var personData []map[string]interface{}
		err = json.Unmarshal(chunk, &personData) //反序列化，使读出来的文件按照原文件格式读出来
		if err != nil {
			fmt.Println("反序列化失败,错误原因: ", err)
			return nil
		}
		return personData
	} else {
		fmt.Println("数据文件不存在")
		return nil
	}
}

/*
	IoutilWriteFile() ioutil.WriteFile方法写文件
		函数传入形参与返回值
			func WriteFile(filename string, data []byte, perm fs.FileMode) error {
				return os.WriteFile(filename, data, perm)
			}
		优点
			1、该方法写文件时不需要判断文件是否存在,如果文件不存在 会自动创建，反之，则会覆盖原来的文件内容
			2、支持多种数据形式的写入，可以写入二进制文件、文本文件等
			3、它提供写文件失败时返回错误信息
		缺点
			1、将整个文件内容读取到内存中，进行修改和写入操作，如果文件过大，会导致内存占用过高
			2、使用的是标准库中的锁机制，因此在高并发场景下，可能存在文件并发写入效率低下的问题。
	IoutilReadFile() ioutil.ReadFile()方法读文件
		函数传入的形参与返回值
			func ReadFile(filename string) ([]byte, error) {
				return os.ReadFile(filename)
			}
		优点
			1、读取文件只需要传入一个文件名作为形参，会将文件的内容作为一个字节数组返回，可以进行反序列化操作
			2、不需要使用os.Open()函数与defer Close()函数打开与关闭文件 这个操作系统自动帮我们完成
		缺点
			1、只适合读取小文件，不适合读取大文件
*/
func IoutilWriteFile(fileName, data string) {
	if err := ioutil.WriteFile(fileName, []byte(data), 0777); err != nil {
		fmt.Println("write file failed", err)
	} else {
		fmt.Println("write file success")
	}
}
func IoutilReadFile(fileName string) []map[string]interface{} {
	var chunk []byte
	var personData []map[string]interface{}
	if Exists(fileName) {
		data, err := ioutil.ReadFile(fileName)
		if err != nil {
			fmt.Println("读取文件失败", err)
		}
		chunk = append(chunk, data...)
	}
	err := json.Unmarshal(chunk, &personData)
	if err != nil {
		fmt.Println("反序列化失败", err)
	}
	return personData
}

/*

 */
func BufioNewWriter(fileName string, databyte []byte) {
	if Exists(fileName) {
		file, err := os.OpenFile(fileName, os.O_APPEND, 0666)
		if err != nil {
			fmt.Println("打开文件失败", err)
		}
		defer file.Close()
		write := bufio.NewWriter(file)
		//	写文件 通过write调用Write方法 且Write()实际上是一个空接口类型封装的函数 Write(p []byte) (n int, err error)
		n, err := write.Write(databyte) //将数据写入到缓存
		if err != nil {
			fmt.Println("write file failed", err)
			return
		}
		fmt.Println("write file success", n)
		write.WriteString("\n") //换行写入文件
		write.Flush()           //将缓存的内容写入文件
	}
}
func BufioNewReader(fileName string) {
	if Exists(fileName) {
		file, err := os.Open(filedatapath)
		if err != nil {
			fmt.Println("打开文件失败", err)
			return
		}
		defer file.Close()
		reader := bufio.NewReader(file)
		for {
			line, err := reader.ReadString('\n')
			if err != nil && err != io.EOF {
				fmt.Println("read failed", err)
				return
			}
			if len(line) == 0 {
				fmt.Println("文件读完了")
				//fmt.Println(line)
				break
			}
			fmt.Println(line)
		}
	}
	return
}

func main() {
	persondata := createdata()
	if testbyte, err := json.Marshal(persondata); err != nil { //通过序列化将切片类型的map转为字节切片类型
		fmt.Println("序列化为切片类型的字节失败")
	} else {
		//读写文件方法一 file.WriteString file.Read
		//writefile(filedatapath, string(testbyte))
		//persondataunmarshal := Readfile(filedatapath)
		//fmt.Println("读文件-->反序列化后的输出:", persondataunmarshal)

		////	读写文件方法二 ioutil.WriteFile()
		//IoutilWriteFile(filedatapath, string(testbyte))
		//persondataunmarshal := IoutilReadFile(filedatapath)
		//fmt.Println("读文件-->反序列化后的输出:", persondataunmarshal)
		BufioNewWriter(filedatapath, testbyte)
		BufioNewReader(filedatapath)
	}
}
